        title  "Interval Clock Interrupt"
;++
;
; Copyright (c) Microsoft Corporation. All rights reserved. 
;
; You may only use this code if you agree to the terms of the Windows Research Kernel Source Code License agreement (see License.txt).
; If you do not agree to the terms, do not use the code.
;
;
; Module Name:
;
;    clockint.asm
;
; Abstract:
;
;    This module implements the code necessary to field and process the
;    interval clock interrupt.
;
;--

.586p
        .xlist
KERNELONLY  equ     1
include ks386.inc
include callconv.inc                    ; calling convention macros
include i386\kimacro.inc
include mac386.inc
        .list

        EXTRNP  Kei386EoiHelper
        EXTRNP  HalRequestSoftwareInterrupt,1,IMPORT,FASTCALL
        EXTRNP  _HalEndSystemInterrupt,2,IMPORT
        extern  _ExpInterlockedPopEntrySListEnd@0:PROC
        extrn   _ExpInterlockedPopEntrySListResume@0:PROC
        extrn   _KeTimeIncrement:DWORD
        extrn   _KeMaximumIncrement:DWORD
        extrn   _KeTickCount:DWORD
        extrn   _KeTimeAdjustment:DWORD
        extrn   _KiAdjustDpcThreshold:DWORD
        EXTRNP  KiCheckForSListAddress,1,,FASTCALL
        extrn   _KiIdealDpcRate:DWORD
        extrn   _KiMaximumDpcQueueDepth:DWORD
        extrn   _KiTickOffset:DWORD
        extrn   _KiTimerTableListHead:DWORD
        extrn   _KiProfileListHead:DWORD
        extrn   _KiProfileLock:DWORD
        extrn   _KiProfileInterval:DWORD
        extrn   _KdDebuggerEnabled:BYTE
        EXTRNP  _DbgBreakPoint
        EXTRNP  _DbgBreakPointWithStatus,1
        EXTRNP  _KdPollBreakIn
        EXTRNP  _KiDeliverApc,3
        extrn   _KeI386MachineType:DWORD
        extrn   _PPerfGlobalGroupMask:DWORD
        EXTRNP  PerfProfileInterrupt,2,,FASTCALL

if DBG
        extrn   _DbgPrint:near
        extrn   _KiDPCTimeout:DWORD
        extrn   _MsgDpcTimeout:BYTE
endif

_DATA   SEGMENT  DWORD PUBLIC 'DATA'
public  ProfileCount
ProfileCount    DD      0

_DATA   ends

        page ,132
        subttl  "Update System Time"

_TEXT$00   SEGMENT DWORD PUBLIC 'CODE'
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
;++
;
; VOID
; KeUpdateSystemTime (
;       IN KIRQL PreviousIrql,
;       IN KTRAP_FRAME TrapFrame
;       )
;
; Routine Description:
;
;    This routine is entered as the result of an interrupt generated by CLOCK2.
;    Its function is to update the system time and check to determine if a timer
;    has expired.
;
;    N.B. This routine is executed on a single processor in a multiprocessor
;       system. The remainder of the processors only execute the quantum end
;       and runtime update code.
;
;    N.B. This routine is not called, but directly jumped to.  Thus, there
;       is no return address.  It returns via the INTERRUPT_EXIT macro.
;
; Arguments:
;
;    PreviousIrql   (esp)   - supplies previous irql of system
;
;    HardwareVector (esp+4) - supplies hardware vector for EndSystemInterrupt
;
;    TrapFrame      (esp+8) - supplies base of trap frame
;
;    EAX is the TimeIncrement value
;
;    EBP is a pointer to the trap frame
;
;
; Environment:
;
;    IRQL = CLOCK_LEVEL
;
; Return Value:
;
;    None.
;
;--
cPublicProc _KeUpdateSystemTime     ,0

.FPO (2, 0, 0, 0, 0, 1) ; treat params as locals since functions is JMPed too

if DBG
        cmp     byte ptr PCR[PcPrcbData+PbSkipTick], 0
        jnz     kust_skiptick
endif

;
; Update interrupt time.
;
; N.B. The interrupt time is updated in a very strict manner so that an
;      interlock does not have to be used in an MP system to read time.
;

        mov     ecx,USER_SHARED_DATA    ; get address of user shared data
        mov     edi,[ecx].UsInterruptTime+0 ; get low interrupt time
        mov     esi,[ecx].UsInterruptTime+4 ; get high interrupt time
        add     edi,eax                 ; add time increment
        adc     esi,0                   ; propagate carry
        mov     [ecx].UsInterruptTime+8,esi ; store high 2 interrupt time
        mov     [ecx].UsInterruptTime+0,edi ; store low interrupt time
        mov     [ecx].UsInterruptTime+4,esi ; store high 1 interrupt time

ifndef NT_UP

   lock sub     _KiTickOffset,eax       ; subtract time increment

else

        sub     _KiTickOffset,eax       ; subtract time increment

endif

        mov     eax,_KeTickCount+0      ; get low tick count
        mov     ebx,eax                 ; copy low tick count
        jg      kust10                  ; if greater, not complete tick

;
; Update system time.
;
; N.B. The system time is updated in a very strict manner so that an
;      interlock does not have to be used in an MP system to read time.
;

        mov     ebx,USER_SHARED_DATA    ; get address of user shared data
        mov     ecx,[ebx].UsSystemTime+0 ; get low system time
        mov     edx,[ebx].UsSystemTime+4 ; get high system time
        add     ecx,_KeTimeAdjustment   ; add time increment
        adc     edx,0                   ; propagate carry
        mov     [ebx].UsSystemTime+8,edx ; store high 2 system time
        mov     [ebx].UsSystemTime+0,ecx ; store low system time
        mov     [ebx].UsSystemTime+4,edx ; store high 1 system time
        mov     ebx,eax                 ; restore low tick count

;
; Update tick count.
;
; N.B. The tick count is updated in a very strict manner so that an
;      interlock does not have to be used in an MP system to read count.
;

        mov     ecx,eax                 ; copy low tick count
        mov     edx,_KeTickCount+4      ; get high tick count
        add     ecx,1                   ; increment tick count
        adc     edx,0                   ; propagate carry
        mov     _KeTickCount+8,edx      ; store high 2 tick count
        mov     _KeTickCount+0,ecx      ; store low tick count
        mov     _KeTickCount+4,edx      ; store high 1 tick count
        mov     USERDATA[UsTickCount]+8, edx ; store USD high 2 tick count
        mov     USERDATA[UsTickCount]+0, ecx ; store USD low tick count
        mov     USERDATA[UsTickCount]+4, edx ; store USD high 1 tick count

;
; Check to determine if a timer has expired.
; (edi:esi) = KiInterruptTime
; (eax) = KeTickCount.LowPart
; (ebx) = KeTickCount.LowPart
;

        .errnz  (TIMER_ENTRY_SIZE - 16)

        and     eax,TIMER_TABLE_SIZE-1  ; isolate current hand value
        shl     eax, 4                  ; compute timer entry offset
        cmp     esi,[eax]+_KiTimerTableListHead+TtTime+4 ; compare high due time 
        jb      short kust5             ; if below, timer has not expired
        ja      short kust15            ; if above, timer has expired
        cmp     edi,[eax]+_KiTimerTableListHead+TtTime ; compare low due time
        jae     short kust15            ; if above or equal, timer has expired
kust5:  inc     ebx                     ; advance hand value to next entry
        mov     eax, ebx                ;

;
; Check to determine if a timer has expired.
; (edi:esi) = KiInterruptTime
; (eax) = bucket
; (ebx) = KeTickCount.LowPart
;

kust10: and     eax,TIMER_TABLE_SIZE-1  ; isolate current hand value
        shl     eax, 4                  ; compute timer entry offset
        cmp     esi,[eax]+_KiTimerTableListHead+TtTime+4 ; compare high due time 
        jb      kustxx                  ; if below, timer has not expired
        ja      short kust15            ; if above, timer has expired
        cmp     edi,[eax]+_KiTimerTableListHead+TtTime ; compare low due time
        jb      kustxx                  ; if below, timer has not expired
kust15:                                 ;

;
; Timer has expired, put timer expiration DPC in the current processor's DPC
; queue.
;
; (ebx) = KeTickCount.LowPart
;

        mov     ecx,PCR[PcPrcb]         ; get processor control block address
        cmp     dword ptr [ecx]+PbTimerRequest, 0 ; check if expiration active
        jne     short kustxx            ; if ne, expiration already active
        mov     [ecx]+PbTimerRequest, esp ; set timer request
        mov     [ecx]+PbTimerHand, ebx  ; set timer hand value
        mov     ecx, DISPATCH_LEVEL     ; request dispatch interrupt
        fstCall HalRequestSoftwareInterrupt ;

;
; If the debugger is enabled, check if a break is requested.
;

kustxx: cmp     _KdDebuggerEnabled, 0   ; check if a debugger is enabled
        jnz     short kust45            ;if nz, debugger is enabled

kust30: cmp     _KiTickOffset,0         ; check if full tick
        jg      short Kust40            ; if not less, not a full tick

        mov     eax,_KeMaximumIncrement ; get maximum time increment
        add     _KiTickOffset,eax       ; add maximum tine to residue

;
; call KeUpdateRunTime to do the actual work
;

; TOS const PreviousIrql
        push    [esp]
        call    _KeUpdateRunTime@4

;
; Do interrupt exit processing
;

        INTERRUPT_EXIT

kust40:
        inc     dword ptr PCR[PcPrcbData+PbInterruptCount]
        INTERRUPT_EXIT

kust45: stdCall _KdPollBreakIn
        or      al,al
        jz      short kust30
        stdCall _DbgBreakPointWithStatus,<DBG_STATUS_CONTROL_C>
        jmp     short kust30

if DBG

kust_skiptick:
        mov     byte ptr PCR[PcPrcbData+PbSkipTick], 0
        jmp     short kust40

endif

stdENDP _KeUpdateSystemTime


        page ,132
        subttl  "Update Thread and Process Runtime"
;++
;
; Routine Description:
;
;    This routines does the actual work to update the runtime of the current
;    thread, update the runtime of the current thread's process, and
;    decrement the current thread's quantum.
;
;    It also updates the system global counters for user and kernel mode time.
;
;    It increments InterruptCount so that clock ticks get counted as
;    interrupts.
;
; Arguments:
;
;    esp+4 constant PreviousIrql
;
;    ebp MUST point to the machine state frame.
;
; Return Value:
;
;    None.
;
;--

cPublicProc _KeUpdateRunTime   ,1
cPublicFpo 1, 1

        mov     eax, PCR[PcSelfPcr]
if DBG
        cmp     byte ptr [eax]+PcPrcbData+PbSkipTick, 0
        jnz     kutp_skiptick
endif
        push    ebx                     ; we will destroy ebx
        inc     dword ptr [eax]+PcPrcbData+PbInterruptCount
        mov     ebx, [eax]+PcPrcbData+PbCurrentThread ; (ebx)->current thread
        mov     ecx, ThApcState+AsProcess[ebx]
                                        ; (ecx)->current thread's process

        test    dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK
        jne     Kutp20                  ; if ne, user mode

        test    byte ptr [ebp]+TsSegCs, MODE_MASK ; test if prev mode was kernel
        jne     Kutp20                  ; if ne, user mode

;
; Update the total time spent in kernel mode
;

        inc     dword ptr [eax].PcPrcbData.PbKernelTime
        cmp     byte ptr [esp+8], DISPATCH_LEVEL
        jc      short Kutp4             ; OldIrql<2, then kernel
        ja      short Kutp3             ; OldIrql>2, then interrupt

        cmp     byte ptr PCR[PcPrcbData.PbDpcRoutineActive], 0
        jz      short Kutp4             ; Executing Dpc?, no then thread time

        inc     dword ptr [eax].PcPrcbData.PbDpcTime
if DBG
;
; Check for dpcs which run for too long
;

        inc     dword ptr [eax].PcPrcbData.PbDebugDpcTime
        mov     edx, _KiDPCTimeout
        cmp     dword ptr [eax].PcPrcbData.PbDebugDpcTime, edx
        jc      Kutp50                  ; Jump if not over limit

;
; Dpc time has exceeded the allowed quanta
;

        push    offset FLAT:_MsgDpcTimeout ; push message address
        call    _DbgPrint               ; print debug message
        add     esp, 1 * 4              ; remove arguments from stack

        cmp     _KdDebuggerEnabled, 0   ; check if debugger enabled
        je      short Kutp6             ; if eq, no debugger, continue
        stdCall _DbgBreakPoint          ; break into debugger

Kutp6:  mov     eax, PCR[PcSelfPcr]     ; restore PCR address
        mov     dword ptr [eax].PcPrcbData.PbDebugDpcTime, 0 ; Reset Time
endif
        jmp     Kutp50

ALIGN 4
Kutp3:
;
; Update the time spent at interrupt time for this processor
;

        inc     dword ptr [eax].PcPrcbData.PbInterruptTime
        jmp     Kutp50

ALIGN 4
Kutp4:

;
; Update the time spent in kernel mode for the current thread and the current
; thread's process.
;
        inc     dword ptr [ebx]+ThKernelTime
        jmp     Kutp50


;
; Update total time spent in user mode
;

ALIGN 4
Kutp20:
        inc     dword ptr [eax].PcPrcbData.PbUserTime
;
; Update the time spend in user mode for the current thread and the current
; thread's process.
;

        inc     dword ptr [ebx]+ThUserTime

;
; Update the DPC request rate which is computed as the average between
; the previous rate and the current rate.
;

ALIGN 4
Kutp50: mov     ecx, [eax].PcPrcbData.PbDpcCount ; get current DPC count
        mov     edx, [eax].PcPrcbData.PbDpcLastCount ; get last DPC count
        mov     [eax].PcPrcbData.PbDpcLastCount, ecx ; set last DPC count
        sub     ecx, edx                ; compute count during interval
        add     ecx, [eax].PcPrcbData.PbDpcRequestRate ; compute sum
        shr     ecx, 1                  ; average current and last
        mov     [eax].PcPrcbData.PbDpcRequestRate, ecx ; set new DPC request rate

;
; If the current DPC queue depth is not zero, a DPC routine is not active,
; and a DPC interrupt has not been requested, then request a dispatch
; interrupt, decrement the maximum DPC queue depth, and reset the threshold
; counter if appropriate.
;

        cmp     dword ptr [eax].PcPrcbData.PbDpcQueueDepth, 0 ; check queue depth
        je      short Kutp53            ; if eq, DPC queue depth is zero
        cmp     byte ptr [eax].PcPrcbData.PbDpcRoutineActive, 0 ; check if DPC active
        jne     short Kutp53            ; if ne, DPC routine active
        cmp     byte ptr [eax].PcPrcbData.PbDpcInterruptRequested, 0 ; check if interrupt
        jne     short Kutp53            ; if ne, DPC routine active
        mov     ecx, DISPATCH_LEVEL     ; request a dispatch interrupt
        fstCall HalRequestSoftwareInterrupt ;
        mov     eax, PCR[PcSelfPcr]     ; restore address of current PCR
        mov     ecx, [eax].PcPrcbData.PbDpcRequestRate ; get DPC request rate
        mov     edx, _KiAdjustDpcThreshold ; reset initial threshold counter
        mov     [eax].PcPrcbData.PbAdjustDpcThreshold, edx ;
        cmp     ecx, _KiIdealDpcRate    ; test if current rate less than ideal
        jge     short Kutp55            ; if ge, rate greater or equal ideal
        cmp     [eax].PcPrcbData.PbMaximumDpcQueueDepth, 1 ; check if depth one
        je      short Kutp55            ; if eq, maximum depth is one
        dec     dword ptr [eax].PcPrcbData.PbMaximumDpcQueueDepth ; decrement depth
        jmp     short Kutp55            ;

;
; The DPC queue is empty or a DPC routine is active or a DPC interrupt
; has been requested. Count down the adjustment threshold and if the
; count reaches zero, then increment the maximum DPC queue depth, but
; no above the initial value and reset the adjustment threshold value.
;

Kutp53: dec     dword ptr [eax].PcPrcbData.PbAdjustDpcThreshold ; decrement threshold
        jnz     short Kutp55            ; if nz, threshold not zero
        mov     ecx, _KiAdjustDpcThreshold ; reset initial threshold counter
        mov     [eax].PcprcbData.PbAdjustDpcThreshold, ecx ;
        mov     ecx, _KiMaximumDpcQueueDepth ; get maximum DPC queue depth
        cmp     ecx, [eax].PcPrcbData.PbMaximumDpcQueueDepth ; check depth
        je      short Kutp55            ; if eq, already a maximum level
        inc     dword ptr [eax].PcPrcbData.PbMaximumDpcQueueDepth ; increment maximum depth

;
; Decrement current thread quantum and check to determine if a quantum end
; has occurred.
;

ALIGN 4
Kutp55: sub     byte ptr [ebx]+ThQuantum, CLOCK_QUANTUM_DECREMENT ; decrement quantum
        jg      Kutp75                      ; if > 0, time remaining on quantum

;
; Set quantum end flag and initiate a dispatcher interrupt on the current
; processor.
;

        cmp     ebx,[eax].PcPrcbData.PbIdleThread ; check if idle thread
        jz      Kutp75                      ; if z, then idle thread
        mov     byte ptr [eax].PcPrcbData.PbQuantumEnd, 1 ; set quantum end indicator
        mov     ecx, DISPATCH_LEVEL         ; request dispatch interrupt
        fstCall HalRequestSoftwareInterrupt ;
Kutp75:                                     ;
        pop     ebx                         ;
        stdRET    _KeUpdateRunTime          ;

if DBG
kutp_skiptick:
        mov     byte ptr [eax]+PcPrcbData+PbSkipTick, 0
        stdRET    _KeUpdateRunTime
endif

stdENDP _KeUpdateRunTime


;++
;
;   PROFILING SUPPORT
;
;--


;++
;
; VOID
; KeProfileInterrupt (
;       IN PKTRAP_FRAME TrapFrame,
;       )
;
; Routine Description:
;
;    This procedure is the ISR for the profile sampling interrupt,
;    which for x86 machines is driven off the 8254 timer1 channel 0.
;
;    The procedure scans the list of profile objects, looking for those
;    which match the address space and return program counter captured
;    at entry.  For each object that does match, the counter in its
;    profile buffer matching the bucket the PC falls into is computed,
;    and that counter is incremented.
;
;    N.B. This routine is executed on all processors in a multiprocess
;       system.
;
; Arguments:
;
;    Return Address (esp)
;
;    TrapFrame (esp+4) - supplies pointer to profile trap frame
;
; Environment:
;
;    IRQL = KiProfileIrql
;
;
; Return Value:
;
;    None.
;
;    WARNING: Uses ALL registers
;
;--

cPublicProc _KeProfileInterrupt ,1
;
; rearrange arguments to pass a source of 0 to KeProfileInterruptWithSource
;
        pop     eax     ; return code in eax
        pop     ebx     ; trap frame in ebx
        push    0       ; push source of 0 (ProfileTime)
        push    ebx     ; push trap frame
        push    eax     ; push return address
        jmp     short   _KeProfileInterruptWithSource@8
stdENDP _KeProfileInterrupt

;++
;
; VOID
; KeProfileInterruptWithSource (
;       IN PKTRAP_FRAME TrapFrame,
;       IN KPROFILE_SOURCE ProfileSource
;       )
;
; Routine Description:
;
;    This procedure is the ISR for the multiple-source profile interrupt.
;
;    Since no x86 HAL currently implements any source other than the
;    clock interrupt, this routine is just a stub that calls KeProfileInterrupt
;
; Arguments:
;
;    Return Address (esp)
;
;    TrapFrame (esp+4) - supplies pointer to profile trap frame
;
;    ProfileSource (esp+8) - supplies source of profile interrupt
;
; Environment:
;
;    IRQL = KiProfileIrql
;
;
; Return Value:
;
;    None.
;
;    WARNING: Uses ALL registers
;
;--
cPublicProc _KeProfileInterruptWithSource,2

kipieip         equ     <dword ptr [ebp+TsEip]>
kipsegcs        equ     <word ptr [ebp+TsSegCs]>
kipeflags       equ     <dword ptr [ebp+TsEFlags]>

        mov     ebp, dword ptr [esp+4]  ; (ebp)-> trap frame
        inc     dword ptr PCR[PcPrcbData+PbInterruptCount]

        cmp     _PPerfGlobalGroupMask, 0 ; check if event tracing is on
        je      short kipi03

        ;; add profile interrupt to perfinfo
        mov        ecx, [esp+8]
        mov        edx,kipieip
        fstCall    PerfProfileInterrupt
        mov     ebp, dword ptr [esp+4]  ; (ebp)-> trap frame

;
; Reset EIP if it is found to be within the kernel interlocked pop entry slist
; code.  Otherwise profile objects (queued by the user) could be used to corrupt
; a kernel slist.
;

kipi03:
        cmp     kipieip, offset FLAT:_ExpInterlockedPopEntrySListResume@0
        jb      kipi04
        cmp     kipieip, offset FLAT:_ExpInterlockedPopEntrySListEnd@0
        ja      kipi04
        mov     ecx, ebp
        fstCall KiCheckForSListAddress

kipi04:

ifndef NT_UP
        lea     eax,_KiProfileLock
kipi05: ACQUIRE_SPINLOCK    eax,kipi96
endif

;
;   Update profile data
;
;   NOTE:
;   System and Process update loops are duplicates, to avoid overhead
;   of call instruction in what could be very high freq. interrupt.
;   be sure to update both loops for changes.
;
;   NOTE:
;   The process loop contains code to update segment profile objects.
;   This code is not present in the system loop, because we do not
;   allow attachment of profile objects for non-flat segments on a
;   system wide basis.
;
;   NOTE:
;   Profiling in V86 mode is handled by converting the CS:IP value to
;   a linear address (CS<<4 + IP)
;

        inc     ProfileCount            ; total number of hits

;
;   Update system profile entries
;

        mov     ebx, kipieip
        mov     edx,offset FLAT:_KiProfileListHead
        mov     esi,[edx].LsFlink       ; (esi) -> profile object
ifndef NT_UP
        mov     edi, PCR[PcSetMember]   ; (edi) = current processor
endif
        mov     ecx, [esp+8]            ; (cx) = profile source
        cmp     esi,edx
        je      kipi30                  ; end of system list, go do process

;
;   (ebx) = sample program counter
;   (esi) -> profile object
;

ALIGN 4
kipi10: cmp     ebx,[esi+PfRangeBase-PfProfileListEntry]    ; >= base?
        jb      kipi20                                      ; no, skip entry
        cmp     ebx,[esi+PfRangeLimit-PfProfileListEntry]   ; < limit?
        jae     kipi20                                      ; no, skip entry
        cmp     cx,word ptr [esi+PfSource-PfProfileListEntry]       ; == source?
        jne     kipi20                                      ; no, skip entry
ifndef NT_UP
        test    edi,[esi+PfAffinity-PfProfileListEntry]     ; affinity match?
        jz      kipi20                                      ; no, skip entry
endif

;
;   RangeBase <= program counter < RangeLimit, we have a hit
;

        sub     ebx,[esi+PfRangeBase-PfProfileListEntry]    ; (ebx) = offset in profile range
        mov     cl,[esi+PfBucketShift-PfProfileListEntry]
        shr     ebx,cl
        and     ebx,NOT 3               ; (ebx) = offset of counter for bucket
        mov     edi,[esi+PfBuffer-PfProfileListEntry] ; (edi) -> buffer
        inc     dword ptr [edi+ebx]     ; record hit
        mov     ebx, kipieip            ; (ebx) = sample pc
        mov     ecx, [esp+8]            ; (cx) = profile source
ifndef NT_UP
        mov     edi, PCR[PcSetMember]   ; (edi) = current processor
endif


;
;   Go to next entry
;

ALIGN 4
kipi20: mov     esi,[esi].LsFlink       ; (esi) -> profile object
        cmp     esi,edx
        jne     kipi10                  ; not end of list, repeat


;
;   Update process profile entries
;   (ebx) = sample program counter
;

ALIGN 4
kipi30: mov     eax,PCR[PcPrcbData+PbCurrentThread] ; (eax)-> current thread
        mov     eax,ThApcState+AsProcess[eax]       ; (eax)-> current process
        lea     edx,[eax]+PrProfileListHead         ; (edx)-> listhead
        mov     esi,[edx].LsFlink                   ; (esi)-> profile object
        cmp     esi,edx
        je      kipi60                              ; process list end, return

;
;   Check for 16 bitness
;
        movzx   ecx,word ptr kipsegcs
        test    kipeflags,EFLAGS_V86_MASK
        jnz     kipi100                             ; convert cs:ip to linear

        cmp     cx,KGDT_R0_CODE
        je      short kipi40

        cmp     cx,KGDT_R3_CODE or RPL_MASK
        jne     kipi110

;
;   (ebx) = sample program counter
;   (esi) -> profile object
;

ALIGN 4
kipi40: cmp     [esi+PfSegment-PfProfileListEntry],word ptr 0 ; flat object?
        jne     kipi50                                      ; no, skip entry
        cmp     ebx,[esi+PfRangeBase-PfProfileListEntry]    ; >= base?
        jb      kipi50                                      ; no, skip entry
        cmp     ebx,[esi+PfRangeLimit-PfProfileListEntry]   ; < limit?
        jae     kipi50                                      ; no, skip entry
        mov     ecx, [esp+8]                                ; (cx) = profile source
        cmp     cx,word ptr [esi+PfSource-PfProfileListEntry]       ; == source?
        jne     kipi50                                      ; no, skip entry
ifndef NT_UP
        mov     edi,PCR[PcSetMember]                        ; (edi) = set member
        test    edi,[esi+PfAffinity-PfProfileListEntry]     ; affinity match?
        jz      kipi50                                      ; no, skip entry
endif


;
;   RangeBase <= program counter < RangeLimit, we have a hit
;

        sub     ebx,[esi+PfRangeBase-PfProfileListEntry]    ; (ebx) = offset in profile range
        mov     cl,[esi+PfBucketShift-PfProfileListEntry]
        shr     ebx,cl
        and     ebx,NOT 3               ; (ebx) = offset of counter for bucket
        mov     edi,[esi+PfBuffer-PfProfileListEntry] ; (edi) -> buffer
        inc     dword ptr [edi+ebx]     ; record hit
        mov     ebx, kipieip            ; (ebx) = sample pc
        mov     ecx, [esp+8]                                ; (cx) = profile source

;
;   Go to next entry
;

ALIGN 4
kipi50: mov     esi,[esi].LsFlink       ; (esi) -> profile object
        cmp     esi,edx
        jne     kipi40                  ; not end of list, repeat

ALIGN 4
kipi60:

ifndef  NT_UP
        lea     eax,_KiProfileLock
        RELEASE_SPINLOCK    eax
endif
        stdRet  _KeProfileInterruptWithSource

ifndef NT_UP
ALIGN 4
kipi96: SPIN_ON_SPINLOCK    eax,kipi05,,DbgMp
endif

ALIGN 4
kipi100:
        shl     ecx,4                   ; segment -> paragraph
        add     ebx,ecx                 ; paragraph offset -> linear
        jmp     kipi40

;
;   Update segment profile objects
;

;
;   (ebx) = sample program counter
;   (esi) -> profile object
;

ALIGN 4
kipi110:
        cmp     [esi+PfSegment-PfProfileListEntry],ecx      ; This segment?
        jne     kipi120                                     ; no, skip entry
        cmp     ebx,[esi+PfRangeBase-PfProfileListEntry]    ; >= base?
        jb      kipi120                                     ; no, skip entry
        cmp     ebx,[esi+PfRangeLimit-PfProfileListEntry]   ; < limit?
        jae     kipi120                                     ; no, skip entry
        mov     ecx, [esp+8]                                ; (cx) = profile source
        cmp     cx,word ptr [esi+PfSource-PfProfileListEntry]       ; == source?
        jne     kipi120                                     ; no, skip entry
ifndef NT_UP
        mov     edi,PCR[PcSetMember]                        ; (edi) = set member
        test    edi,[esi+PfAffinity-PfProfileListEntry]     ; affinity match?
        jnz     kipi120                                     ; no, skip entry
endif

;
;   RangeBase <= program counter < RangeLimit, we have a hit
;

        sub     ebx,[esi+PfRangeBase-PfProfileListEntry]    ; (ebx) = offset in profile range
        mov     cl,[esi+PfBucketShift-PfProfileListEntry]
        shr     ebx,cl
        and     ebx,NOT 3               ; (ebx) = offset of counter for bucket
        mov     edi,[esi+PfBuffer-PfProfileListEntry] ; (edi) -> buffer
        inc     dword ptr [edi+ebx]     ; record hit
        mov     ebx, kipieip            ; (ebx) = sample pc
        mov     cx,kipsegcs             ; ecx = sample cs

;
;   Go to next entry
;

ALIGN 4
kipi120:
        mov     esi,[esi].LsFlink       ; (esi) -> profile object
        cmp     esi,edx
        jne     kipi110                 ; not end of list, repeat

        jmp     kipi60

stdENDP _KeProfileInterruptWithSource
_TEXT$00   ends
        end

